package tool

import (
	"errors"
	"gitee.com/fatzeng/srf_switch_basic_components/util"
	"golang.org/x/sync/errgroup"
)

// FunWith A batch of functions is executed, execution mode control, async
func FunWith(async bool, doWith ...func() error) (err error) {
	defer HandleErr(&err)
	if len(doWith) <= 0 {
		return
	}
	group := errgroup.Group{}
	for _, doWithFun := range doWith {
		if doWithFun == nil {
			continue
		}
		if async {
			group.Go(func() (err error) {
				defer HandleErr(&err)
				return doWithFun()
			})
		} else {
			err = doWithFun()
			if err != nil {
				return err
			}
		}
	}
	return group.Wait()
}

// FunWithAsync Asynchronous execution
func FunWithAsync(doWith ...func() error) (err error) {
	return FunWith(true, doWith...)
}

// FunWithAsync Synchronous execution
func FunWithSync(doWith ...func() error) (err error) {
	return FunWith(false, doWith...)
}

// LockWith Lock the function before executing it
func LockWith(async bool, ld func(), lc func(), doWith ...func() error) (err error) {
	defer HandleErr(&err)
	if ld == nil || lc == nil || doWith == nil {
		return errors.New("not meeting locking conditions")
	}

	ld()
	defer lc()
	return FunWith(async, doWith...)
}

// ReentryLockWith The function can be re-locked before it is executed
func ReentryLockWith(async bool, ld func(interface{}), lc func(interface{}), doWith ...func() error) (err error) {
	defer HandleErr(&err)
	if ld == nil || lc == nil || doWith == nil {
		return errors.New("not meeting locking conditions")
	}
	stackTrace := util.StackPrint()
	lockHolder, err := util.GetGoroutineId(stackTrace)
	if err != nil {
		return err
	}

	ld(lockHolder)
	defer lc(lockHolder)
	return FunWith(async, doWith...)
}

// LockWithAsync Lock the function before it is executed asynchronously
func LockWithAsync(ld func(), lc func(), doWith ...func() error) (err error) {
	return LockWith(true, ld, lc, doWith...)
}

// LockWithSync Lock the function before synchronous execution
func LockWithSync(ld func(), lc func(), doWith ...func() error) (err error) {
	return LockWith(false, ld, lc, doWith...)
}

// LockReentryWithAsync The function is executed asynchronously and has a reentrant lock before it
func LockReentryWithAsync(reentryLock *ReentryLock, doWith ...func() error) (err error) {
	if reentryLock == nil {
		return errors.New("not meeting locking conditions")
	}
	return ReentryLockWith(true, reentryLock.Lock, reentryLock.UnLock, doWith...)
}

// LockReentryWithSync The reentrant lock can be placed before the function is executed synchronously
func LockReentryWithSync(reentryLock *ReentryLock, doWith ...func() error) (err error) {
	if reentryLock == nil {
		return errors.New("not meeting locking conditions")
	}
	return ReentryLockWith(false, reentryLock.Lock, reentryLock.UnLock, doWith...)
}
